package asm;

import org.objectweb.asm.*;
import org.objectweb.asm.commons.AdviceAdapter;
import org.objectweb.asm.commons.Method;

import java.io.FileInputStream;
import java.io.FileOutputStream;

import static org.objectweb.asm.Opcodes.*;

public class ASMGenDemo {
    public static void main(String[] args) throws Exception {
        //1.生成Class字节码
        byte[] genClassByte = genClass();
        try {
            //输出Class字节码文件
            FileOutputStream fos = new FileOutputStream("src/asm/User.class");
            fos.write(genClassByte);
            fos.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
        testClass("asm.test.User",genClassByte);
    }

    /**
     * 生成一个输出Hello的Class字节码
     */
    public static byte[] genPrintHelloClass() throws Exception {

        ClassWriter classWriter = new ClassWriter(-1);
        MethodVisitor methodVisitor;
        classWriter.visit(V1_8, ACC_PUBLIC | ACC_SUPER, "asm/User", null, "java/lang/Object", null);
        {
            methodVisitor = classWriter.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null);
            methodVisitor.visitCode();
            Label label0 = new Label();
            methodVisitor.visitLabel(label0);
            methodVisitor.visitLineNumber(3, label0);
            methodVisitor.visitVarInsn(ALOAD, 0);
            methodVisitor.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V", false);
            methodVisitor.visitInsn(RETURN);
            Label label1 = new Label();
            methodVisitor.visitLabel(label1);
            methodVisitor.visitLocalVariable("this", "Lasm/User;", null, label0, label1, 0);
            methodVisitor.visitMaxs(1, 1);
            methodVisitor.visitEnd();
        }
        {
            methodVisitor = classWriter.visitMethod(ACC_PUBLIC | ACC_STATIC, "main", "([Ljava/lang/String;)V", null, null);
            methodVisitor.visitCode();
            Label label0 = new Label();
            methodVisitor.visitLabel(label0);
            methodVisitor.visitLineNumber(7, label0);
            methodVisitor.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;");
            methodVisitor.visitLdcInsn("hello");
            methodVisitor.visitMethodInsn(INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V", false);
            Label label1 = new Label();
            methodVisitor.visitLabel(label1);
            methodVisitor.visitLineNumber(8, label1);
            methodVisitor.visitInsn(RETURN);
            Label label2 = new Label();
            methodVisitor.visitLabel(label2);
            methodVisitor.visitLocalVariable("args", "[Ljava/lang/String;", null, label0, label2, 0);
            methodVisitor.visitMaxs(2, 1);
            methodVisitor.visitEnd();
        }
        classWriter.visitEnd();
        return classWriter.toByteArray();
    }

    public static byte[] genClass() {
        ClassWriter classWriter = new ClassWriter(0);
        classWriter.visit(
                V1_8,
                ACC_PUBLIC | ACC_SUPER,
                "asm/User",
                null,
                "java/lang/Object",
                null);
        {

            MethodVisitor methodVisitor = classWriter.visitMethod(
                    ACC_PUBLIC,
                    "<init>",
                    "()V",
                    null,
                    null);
            methodVisitor.visitCode();
            Label label0 = new Label();
            methodVisitor.visitLabel(label0);
            methodVisitor.visitLineNumber(3, label0);
            methodVisitor.visitVarInsn(ALOAD, 0);
            methodVisitor.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V", false);
            methodVisitor.visitInsn(RETURN);
            Label label1 = new Label();
            methodVisitor.visitLabel(label1);
            methodVisitor.visitLocalVariable("this", "Lasm/User;", null, label0, label1, 0);
            methodVisitor.visitMaxs(1, 1);
            methodVisitor.visitEnd();
        }
        classWriter.visitEnd();
        return classWriter.toByteArray();
    }

    /**
     * 测试运行Class
     *
     * @param name Class完整包名路径
     * @param b    Class字节码
     * @throws Exception
     */
    public static void testClass(String name, byte[] b) throws Exception {
        MyClassLoader myClassLoader = new MyClassLoader();
        Class clazz = myClassLoader.defineClass(name, b);
        Object obj = clazz.newInstance();

        //仅为了测试
        for (java.lang.reflect.Method method : clazz.getMethods()) {
            System.out.println("Method Name:" + method.getName());
            if (method.getName().equals("main")) {
                method.invoke(obj, new String[1]);
                break;
            }
            if (method.getName().equals("hashCode")) {
                Integer code = (Integer) method.invoke(obj, null);
                System.out.println("hashCode code:"+code);
            }


        }
    }

    public static class MyClassLoader extends ClassLoader{
        public Class defineClass(String name, byte[] b) {
            return defineClass(name, b, 0, b.length);
        }
    }
}
